Jenkins Pipeline As Code
目录
[toc]
本节实战
实战名称 |
---|
💘 实战:第一条Pipeline-2023.4.7(测试成功) |
💘 实战:脚本式语法测试-2023.4.7(测试成功) |
💘 实战:测试脚本式语法-2023.4.7(测试成功) |
💘 实战:声明式语法嵌入脚本式语法-2023.4.7(测试成功) |
💘 实战:利用片段生成器生成执行shell命令代码-2023.4.7(测试成功) |
💘 实战:单引号和双引号的区别-2023.4.7(测试成功) |
💘 实践:使用jenkins的内置变量来显示构建名及构建分支-2023.3.23(测试成功) |
💘 实践:输出全局变量BUILD_ID和BUILD_URL (测试成功)-2023.4.8 |
💘 实践:agent配置的几种方法(测试成功)-2023.4.8 |
💘 实践:post{}测试(测试成功)-2023.4.8 |
💘 实践:变量测试(测试成功)-2023.4.8 |
💘 实践:options测试(测试成功)-2023.4.8 |
💘 实践:配置流水线跳过默认代码检出功能-2023.6.26(测试成功) |
💘 实践:parameters测试(测试成功)-2023.4.8 |
💘 实践:input测试(测试成功)-2023.4.8 |
💘 实践:when测试(测试成功)-2023.4.8 |
💘 实践:parallel测试(测试成功)-2023.4.10 |
💘 实战:groovy字符串测试-2023.4.10(测试成功) |
💘 实战:groovy列表测试-2023.4.10(测试成功) |
💘 实战:groovy if语句测试-2023.4.10(测试成功) |
💘 实战:groovy switch语句测试-2023.4.10(测试成功) |
💘 实战:groovy for语句测试-2023.4.10(测试成功) |
💘 实战:groovy while语句测试-2023.4.10(测试成功) |
💘 实战:groovy 异常处理测试-2023.4.10(测试成功) |
💘 实战:groovy 函数测试-2023.4.10(测试成功) |
💘 实践:Jenkins共享库实践(测试成功)-2023.4.11 |
一、pipeline
1、什么是Pipeline
1、Pipeline简介
- Pipeline是Jenkins 2.0版本推出的核心功能;
- Pipeline可以实现以代码的方式定义工作流程;
- 商业价值:
- 组织级别及团队间工作流复用;
- 便于Pipeline开发与维护;
- 减少人工Web页面操作;
如果装了中文插件后,这里可能显示为流水线
。
Jenkins的核心是Pipeline(流水线项目),实现了Pipeline As Code。即我们将构建部署测试等步骤全部以代码的形式写到Jenkinsfile中。Jenkins在运行Pipeline任务的时候会按照Jenkinsfile中定义的代码顺序执行。写Jenkinsfile是一项很重的工作,如果稍不注意很容易造成Jenkins的流水线任务失败。Jenkinsfile类似于Dockerfile,具有一套特定的语法。
在2年前, 18年的时候,我们一直在使用自由风格类型的项目。 每个项目中会有代码库的参数信息。 直到有一天项目的代码块从SVN迁移到了GITLAB,即需要我来操作修改Jenkins作业中的代码库URL信息。 修改的过程不复杂,即在web页面中修改然后保存即可。看似简单的过程,但是有大量的流水线作业,基本上一上午就在点点点。(操作简单,重复性动作太多了)
后来了解到Jenkins的核心特性Pipeline, 用代码的方式描述流水线。 这样我就可以维护多个Jenkinsfile来对应不同类型的项目了。 也实现了一部分项目使用统一的一个Jenkinsfile模板来管理。
2、Pipeline组成
- Jenkinsfile:描述Pipeline的代码文件
- Agent:Pipeline的运行节点
- Stage:Pipeline的阶段
Jenkinsfile:是实现Pipeline as Code的核心功能。 该文件用于描述流水线的过程。
Agent: (是否还记得上次课程添加的JenkinsAgent节点)执行Pipeline的实际节点。
在Jenkins pipeline中,一条流水线是由多个阶段组成的,每个阶段一个stage。例如:构建、测试、部署等等。
3、第一条Pipeline
==💘 实战:第一条Pipeline-2023.4.7(测试成功)==
- 安装Pipeline插件;
- 创建Pipeline类型项目;
- 项目设置,编写Pipeline;
- 构建项目;
让我们开始编写第一条Pipeline吧: 第一条流水线不用太复杂,主要是便于理解流水线的结构。
pipeline {agent anystages{stage("hello"){steps{echo "Hello Jenkins"}}}}
安装Pipeline插件
在创建Pipeline类型的作业的时候,需要提前安装好pipeline插件,不然可能会出现找不到pipeline类型的作业。
进入插件管理, 搜索关键字"pipeline"。安装后重启一下。
创建Pipeline类型的作业,然后用我们上面编写好的代码运行一下吧。
2、Jenkinsfile语法
Jenkinsfile的基本概念和Jenkins Pipeline的语法类型。
1、Jenkinsfile是什么?
功能: 描述Jenkins Pipeline (Jenkinsfile主要是用来存储用于描述Pipeline的代码。 我们将pipeline的描述代码保存到Jenkinsfile这个文件中。 )
存放方式:
将Jenkinsfile 存放在项目设置(原生支持); (虽然实现了PipelineAsCode但是多个作业管理起来不太方便。)
将Jenkinsfile 存放在Git系统(原生支持);(推荐)(即实现了PipelineAsCode也便于统一管理。)
将Jenkinsfile 存放在制品库(需要第三方插件);(制品库上你改起来不太方便。例如一些场景,我们现在写好一些jenkinsfile,不需要子公司去改,那么我们可以把这个文件打成一个包放到制品库里面,同步给他们,然后在流水线里这么去配置。但是如果需要去改的话,那么就需要用到git,git更方便一点。)
2、Pipeline 语法类型
脚本式语法:
适合以Groovy代码的方式描述Pipeline; (对groovy的要求比较高;)
很多功能特性都需要自行写代码实现; (例如后续写一些邮件告警,想要获取流水线状态,可能都需要自己去写代码实现。)
语法简单,没有相对固定的语法,直接写groovy代码;
声明式语法:
语法简单, 固定的语法;
提供很多已经定义好的功能;(流水线成功失败捕获等等)
也可以通过script{} 嵌入脚本式语法;
jenkins语法有两种类型, 脚本式、声明式语法; 脚本式语法基本上都是通过Groovy代码来进行编写的。声明式语法有一些现成的功能可以直接用,减少脚本式语法的复杂性。但是声明式语法也不是完美的,功能固定还是需要脚本式语法来进行扩展才能实现更加灵活的Pipeline。
最佳实践是声明式语法中通过script{}
标签来嵌入脚本式代码。
==💘 实战:脚本式语法测试-2023.4.7(测试成功)==
- 代码
node("build01"){stage('Build'){echo 'Hello world'}stage('Test'){echo 'Hello world'}}
- 构建
==💘 实战:测试脚本式语法-2023.4.7(测试成功)==
- 代码
pipeline {agent anystages {stage('Build') {steps {echo 'Hello World'println("devops")}}stage('Test') {steps {echo 'Hello World'}} }}
- 运行
==💘 实战:声明式语法嵌入脚本式语法-2023.4.7(测试成功)==
- 错误格式代码:
pipeline {agent anystages {stage('Build') {steps {echo 'Hello World'println("devops")name ="RongMei"if(name =="RongMei"){println("true")}}}stage('Test') {steps {echo 'Hello World'}} }}
构建后,会报错的:
- 正确格式代码:
pipeline {agent anystages {stage('Build') {steps {echo 'Hello World'println("devops")script{name ="RongMei"if(name =="RongMei"){println("true")}}}}stage('Test') {steps {echo 'Hello World'}} }}
以上代码运行后是正常的:
3、Pipeline开发工具
很多人看到Jenkins Pipeline的代码后,就已经被劝退了。 感觉代码的难度很高,其实难度并不高。利用好开发工具后面理解起来就更简单些。 选择任意pipeline类型的作业,点击“流水线语法”即可进入pipeline开发工具页面。
1. 片段生成器
当你安装好插件之后,很多插件提供了对应的代码块。 我们可以导航到片段生成器中找到对应的代码块生成。如果没有找到相关的语法,可以检查是否安装配置了相关的插件。
这个工具可以帮助我们生成Pipeline的部分代码,降低Pipeline的开发难度。流水线代码片段生成器, 非常好用。在这里可以找到每个插件以及Jenkins内置的方法的使用方法。使用片段生成器可以根据个人需要生成方法,有些方法来源于插件,则需要先安装相关的插件才能使用哦。
范例:error报错
修改代码:
构建:
范例:利用片段生成器生成执行shell命令代码
==💘 实战:利用片段生成器生成执行shell命令代码-2023.4.7(测试成功)==
- 利用
片段生成器
生成执行shell命令代码
- 代码
pipeline {agent anystages {stage('Build') {steps {echo 'Hello World'println("devops")script{name ="RongMei"if(name =="RongMei"){println("true")}sh 'ls -al'}}}stage('Test') {steps {echo 'Hello World'}} }}
- 运行
范例:单引号和双引号的区别
==💘 实战:单引号和双引号的区别-2023.4.7(测试成功)==
单引号里写变量是不生效的;(和其它编程语言差不多)
注意:这里一定要使用双引号,否则会把其当做字符串打印出的!(linux中单引号与双引号的区别)
双引号:
- 代码
pipeline {agent anystages {stage('Build') {steps {echo 'Hello World'println("devops")script{name ="RongMei"if(name =="RongMei"){println("true")}sh """echo ${name}ls -al"""}}}stage('Test') {steps {echo 'Hello World'}} }}
- 运行
单引号:
- 代码
pipeline {agent anystages {stage('Build') {steps {echo 'Hello World'println("devops")script{name ="RongMei"if(name =="RongMei"){println("true")}sh '''echo ${name}ls -al'''}}}stage('Test') {steps {echo 'Hello World'}} }}
- 运行:
2. 声明式语法生成器
声明式语法式特有的一套语法,如果声明式语法忘记了可以导航到声明式语法生成器
生成对应的代码片段。
范例:生成阶段
编写代码:
构建:
3. 全局变量参考
常见的全局变量
全局变量
pipelineenvparamscurrentBuildscm
有时我们会获取一些项目的参数来做数据处理, 此时可以通过Jenkins提供的内置变量来获取对应的关键信息。
例如:获取当前项目的名称、构建ID、作业URL等等信息。
BUILD_NUMBERBUILD_IDBUILD_DISPLAY_NAMEJOB_NAMEEXECUTOR_NUMBERNODE_NAMEWORKSPACEJENKINS_HOMEJENKINS_URLBUILD_URLJOB_URLdisplayNamedescriptiondurationenv.branchName ="develop"env.commitID ="${UUID.randomUUID().toString()}"println(env.commitID)env.commitID ="${env.commitID.split("-")[0]}"println(env.commitID)currentBuild.displayName ="#${env.branchName}-${env.commitID}"currentBuild.description ="Trigger by user jenkins \nbranch:${env.branchName}"pipeline {agent {label "build"}stages{stage("test"){steps{script{echo "${BUILD_NUMBER}"echo "当前下载代码分支为: ${env.branchName}"}}}}}
运行效果:
全局变量定义并使用
结论:
例如 env.myName ="xyy",myName ="xyy"等语法属于groovy语法,如果直接定义在stages/stage/steps里会报错的,需要在scripts里定义,或者在pipeline{}外面定义。
在pipeline{}外面定义的是全局变量;
在scripts里定义的变量属于局部变量,加上env标识的话,就属于全局变量了;
1、定义在pipeline{}外面
==💘 实践:输出全局变量BUILD_ID和BUILD_URL
(测试成功)-2023.4.8==
- 代码
currentBuild.displayName ="Devops:commitID"currentBuild.description ="Branch:master"pipeline {agent anystages {stage('Build') {steps {echo 'Hello World'println("devops")script{name ="RongMei"if(name =="RongMei"){println("true")}sh """echo ${name}ls -al"""}}}stage('Test') {steps {echo 'Hello World'echo"build_id:${BUILD_ID}"echo "build_url:${BUILD_URL}"}} }}
- 构建
2、定义在scripts{}里面
- 我们首先将全局变量的定义放在
stage
里面:
pipeline{agent {node {label "build"}}options {skipStagesAfterUnstable()}stages{stage("CheckOut"){steps{env.myName ="xyy"script{println("获取代码")echo 'i love you ,xyy'echo "${BUILD_ID}"echo "${env.BUILD_ID}"echo "$BUILD_ID"echo "${myName}"}}}stage("Build"){steps{script{println("运行构建")}}}}post {always{script{println("流水线结束后,经常做的事情")}}success{script{println("流水线成功后,要做的事情")}}failure{script{println("流水线失败后,要做的事情")}}aborted{script{println("流水线取消后,要做的事情")}}}}
结果构建失败了:
- 此时,我们修改下代码,再把其放在script里面,看能否构建成功:
pipeline{agent{node{label"build"}}options{skipStagesAfterUnstable()}stages{stage("CheckOut"){steps{script{env.myName="xyy"echo"${myName}"println("获取代码")echo'i love you ,xyy'echo"${BUILD_ID}"echo"${env.BUILD_ID}"echo"$BUILD_ID"}}}stage("Build"){steps{script{println("运行构建")}}}}post{always{script{println("流水线结束后,经常做的事情")}}success{script{println("流水线成功后,要做的事情")}}failure{script{println("流水线失败后,要做的事情")}}aborted{script{println("流水线取消后,要做的事情")}}}}
可以看到,本次构建成功,原来就是把命令式代码写在stage位置了,导致构建失败!因此也验证了上面的结论脚本式的语法我们都写在script里面
。
- 刚才在检出代码阶段定义的全局变量,是否可以在构建阶段里面使用?(肯定可以的)这边来测试下
pipeline{agent{node{label"build"}}options{skipStagesAfterUnstable()}stages{stage("CheckOut"){steps{script{env.myName="xyy"echo"${myName}"env.branchName="dev"println("获取代码")echo'i love you ,xyy'echo"${BUILD_ID}"echo"${env.BUILD_ID}"echo"$BUILD_ID"}}}stage("Build"){steps{script{println("运行构建")echo"${branchName}"}}}}post{always{script{println("流水线结束后,经常做的事情")}}success{script{println("流水线成功后,要做的事情")}}failure{script{println("流水线失败后,要做的事情")}}aborted{script{println("流水线取消后,要做的事情")}}}}
结论:可以看到,在某一个stage里定义的全局变量,在其他stage里也是可以使用的!(符合预期)
🍀 示例:如何使用命令式语法来定义局部变量
- 代码
pipeline{agent{node{label"build"}}stages{stage("CheckOut"){steps{script{println("获取代码")envType="test"}}}stage("Build"){steps{script{println("运行构建")echo"envType:${envType}"}}}}}
进行构建:
结论:发现在script里定义的局部变量,在其他stage里也是可以使用的。。。😥并且,也是可以在stage里引用局部变量的,也是不会报错的。
- Jenkins web也是可以配置全局变量的,但是不推荐(使用groovy代码就好)
管理Jenkins-配置系统:
4.调试回放流水线
适合调试流水线代码,构建后点击回访可以看到上次构建运行的Pipeline代码,而我们可以通过此编辑器对代码进行修改和调试而不会影响原始的代码。
4、Pipeline核心语法
声明式+脚本式
。
Pipeline核心语法1
pipeline{}
声明式流水线的定义, 一个pipeline{}。
agent{}
定义Pipeline中的运行节点(Jenkins Agent)
pipeline{agent {} }
流水线级别的节点stage{agent{} }
阶段级别的节点使用方式
label:**在特定的名称或者label节点**运行; (标签=分组)
any:在任意节点运行;
none:当pipeline全局指定agent为none,则根据每个stage中定义的agent运行(stage必须指定)。
node:**支持自定义流水线的工作目录**。
pipeline里必须要有agent格式的。
pipeline {agent any }pipeline {agent {label "labelName或者节点名称"}}pipeline {agent none stages{stage('Build'){agent {label "build"} steps {echo "building......"}}}}pipeline {agent {node {label "labelName或者节点名称"customWorkspace "/opt/agent/workspace"}}}
Q:agent {node {label }}
这个语法第一次见,怎么知道有这个语法,是声明语法生成器中的吗?
但是声明式语法生成器中是没有这个配置的。
范例:agent配置的几种方法
==💘 实践:agent配置的几种方法(测试成功)-2023.4.8==
⚠️ 特别注意:
label
后面可以接labelName或者节点名称,都是可以的。
测试过程如下:
- 当前测试环境节点名称及标签如下:
label
后面可以接节点名称
代码:
currentBuild.displayName ="Devops:commitID"currentBuild.description ="Branch:master"pipeline {agent {label "build01"}stages {stage('Build') {steps {echo 'Hello World'println("devops")script{name ="RongMei"if(name =="RongMei"){println("true")}sh """echo ${name}ls -al"""}}}stage('Test') {steps {echo 'Hello World'echo"build_id:${BUILD_ID}"echo "build_url:${BUILD_URL}"}} }}
运行:
label
后面可以接labelName
代码:
currentBuild.displayName ="Devops:commitID"currentBuild.description ="Branch:master"pipeline {agent {label "build"}stages {stage('Build') {steps {echo 'Hello World'println("devops")script{name ="RongMei"if(name =="RongMei"){println("true")}sh """echo ${name}ls -al"""}}}stage('Test') {steps {echo 'Hello World'echo"build_id:${BUILD_ID}"echo "build_url:${BUILD_URL}"}} }}
- 作业运行在任意节点:
代码:
currentBuild.displayName ="Devops:commitID"currentBuild.description ="Branch:master"pipeline {agent anystages {stage('Build') {steps {echo 'Hello World'println("devops")script{name ="RongMei"if(name =="RongMei"){println("true")}sh """echo ${name}ls -al"""}}}stage('Test') {steps {echo 'Hello World'echo"build_id:${BUILD_ID}"echo "build_url:${BUILD_URL}"}} }}
运行:
- 使用node来自定义流水线构建目录
代码:
currentBuild.displayName ="Devops:commitID"currentBuild.description ="Branch:master"pipeline {agent {node{label "build"customWorkspace "/opt/jenkinsagent/workspace/test"}}stages {stage('Build') {steps {echo 'Hello World'println("devops")script{name ="RongMei"if(name =="RongMei"){println("true")}sh """echo ${name}ls -al"""}}}stage('Test') {steps {echo 'Hello World'echo"build_id:${BUILD_ID}"echo "build_url:${BUILD_URL}"}} }}
构建:
- 如果指定一个错误节点或者标签,测试的作业状态时什么样的呢?
代码:
currentBuild.displayName ="Devops:commitID"currentBuild.description ="Branch:master"pipeline {agent {node{label "build02"customWorkspace "/opt/jenkinsagent/workspace/test"}}stages {stage('Build') {steps {echo 'Hello World'println("devops")script{name ="RongMei"if(name =="RongMei"){println("true")}sh """echo ${name}ls -al"""}}}stage('Test') {steps {echo 'Hello World'echo"build_id:${BUILD_ID}"echo "build_url:${BUILD_URL}"}} }}
构建:
- 测试:label虽然不支持正则,agent是可以加变量去选节点的
代码:
currentBuild.displayName ="Devops:commitID"currentBuild.description ="Branch:master"choiceBuildNode ="build"pipeline {agent {node{label "${choiceBuildNode}"customWorkspace "/opt/jenkinsagent/workspace/test"}}stages {stage('Build') {steps {echo 'Hello World'println("devops")script{name ="RongMei"if(name =="RongMei"){println("true")}sh """echo ${name}ls -al"""}}}stage('Test') {steps {echo 'Hello World'echo"build_id:${BUILD_ID}"echo "build_url:${BUILD_URL}"}} } }
运行:
- 运行在jenins节点
stages{}
定义Pipeline的阶段;
pipeline{stages{}}stages>stage>steps
- 关系: stages >stage >steps >script
- 定义:
- stages:包含多个stage阶段
- stage:包含多个steps步骤
- steps:包含一组特定的脚本(加上script后就可以实现在声明式脚本中嵌入脚本式语法了)
currentBuild.displayName ="Devops:commitID"currentBuild.description ="Branch:master"pipeline {agent {node{label "build02"customWorkspace "/opt/jenkinsagent/workspace/test"}}stages {stage('Build') {steps {echo 'Hello World'println("devops")script{name ="RongMei"if(name =="RongMei"){println("true")}sh """echo ${name}ls -al"""}}}stage('Test') {steps {echo 'Hello World'echo"build_id:${BUILD_ID}"echo "build_url:${BUILD_URL}"}} }}
- 注意:
steps
下是没有step的。
代码:
currentBuild.displayName ="Devops:commitID"currentBuild.description ="Branch:master"choiceBuildNode ="build"pipeline {agent {node{label "${choiceBuildNode}"customWorkspace "/opt/jenkinsagent/workspace/test"}}stages {stage('Build') {steps {step{echo "hello"}echo 'Hello World'println("devops")script{name ="RongMei"if(name =="RongMei"){println("true")}sh """echo ${name}ls -al"""}}}stage('Test') {steps {echo 'Hello World'echo"build_id:${BUILD_ID}"echo "build_url:${BUILD_URL}"}} } }
报错:
⚠️ 注意:
- 虽然有的shell命令放在steps里也不会报错的,但是为了代码的规范性及可读性,强烈建议将shell命令都放在script里!(有可能if else放在steps里可能会报错);
- scripts里是存放groovy脚本的;
post{}
根据流水线的最终状态做一些操作
状态:
always: 总是执行 (例如每次执行后清理缓存动作;)
success: 仅流水线成功后执行 (执行成功后,也可触发下一动作,例如触发CD;(远程构建))
failure: 仅流水线失败后执行 (failure/aborted:可能要给开发人员发个邮件。)
aborted: 仅流水线被取消后执行
unstable:不稳定状态,单测失败等
注意:post{}是和states同级的!
pipeline {..........post {always{script{println("流水线结束后,经常做的事情")}}success{script{println("流水线成功后,要做的事情")}}failure{script{println("流水线失败后,要做的事情")}}aborted{script{println("流水线取消后,要做的事情")}}}}
范例:综合脚本
==💘 实践:post{}测试(测试成功)-2023.4.8==
- 代码
currentBuild.displayName ="Devops:commitID"currentBuild.description ="Branch:master"pipeline {agent {node{label "build"customWorkspace "/opt/jenkinsagent/workspace/test"}}stages {stage('Build') {steps {echo 'Hello World'println("devops")script{name ="RongMei"if(name =="RongMei"){println("true")}sh """echo ${name}ls -al"""}}}stage('Test') {steps {echo 'Hello World'echo"build_id:${BUILD_ID}"echo "build_url:${BUILD_URL}"}} }post {always{script{println("流水线结束后,经常做的事情")}}success{script{println("流水线成功后,要做的事情")}}failure{script{println("流水线失败后,要做的事情")}}aborted{script{println("流水线取消后,要做的事情")}}} }
- 运行:
Pipeline核心语法2
environment{}
以声明式脚本语法的方式定义环境变量;
局部变量优先级高于全局变量;
推荐:声明式语法里使用enironment这个语句块来写。
方式1:声明式语法
定义: 通过键值对(k-v)格式定义流水线在运行时的环境变量, 分为流水线级别和阶段级别。
pipeline {agent anyenvironment {NAME="RongMei"VERSION="1.1.10"ENVTYPE="DEV"} stages {stage("build"){environment {VERSION="1.1.20"}steps {script {echo "${VERSION}"echo "${buildUser}"}}}}}
方式2:全局变量可以放在pipeline外面
env.lover="xyy"env.age=18pipeline {agent anyenvironment {NAME="RongMei"VERSION="1.1.10"ENVTYPE="DEV"} stages {stage("build"){environment {VERSION="1.1.20"}steps {script {echo "${VERSION}"echo "lover:${env.lover} age:${env.age}"}}}}}
==💘 实践:变量测试(测试成功)-2023.4.8==
- 代码
buildUser ="jenkins"pipeline {agent anyenvironment {NAME="RongMei"VERSION="1.1.10"ENVTYPE="DEV"} stages {stage("build"){environment {VERSION="1.1.20"}steps {script {echo "${VERSION}"echo "${buildUser}"}}}}}
- 运行
options{}
Pipeline运行时的一些选项
- 设置保存最近的记录
- 禁止并行构建
- 跳过默认的代码检出
- 设定流水线的超时时间(可用于阶段级别)
- 设定流水线的重试次数(可用于阶段级别)
- 设置日志时间输出(可用于阶段级别)
⚠️ 注意:
(以代码的方式定义的配置,需要流水线构建运行后才能看到效果)。
Jenkins需要加载一下,才能够做这个设置。
所以刚才我们定义的这些设置,都得初始化下。第一次初始化时,不用去关心它的状态是成功还是失败,它只是进行一些项目的初始化。
常用配置:
## 设置保存最近的记录options {buildDiscarder(logRotator(numToKeepStr:'1')) }## 禁止并行构建options {disableConcurrentBuilds() }## 跳过默认的代码检出options {skipDefaultCheckout() }## 设定流水线的超时时间(可用于阶段级别)options {timeout(time:1,unit:'HOURS') }## 设定流水线的重试次数(可用于阶段级别)options {retry(3) }## 设置日志时间输出(可用于阶段级别)options {timestamps() }
!
pipeline {options {disableConcurrentBuilds()skipDefaultCheckout()timeout(time:1,unit:'HOURS')}stages {stage("build"){options {timeout(time:5,unit:'MINUTES')retry(3)timestamps()}}}}}
范例:option选项测试
pipeline {agent anyoptions {disableConcurrentBuilds()skipDefaultCheckout()timeout(time:1,unit:'HOURS')buildDiscarder logRotator(artifactDaysToKeepStr:'',artifactNumToKeepStr:'',daysToKeepStr:'30',numToKeepStr:'5')}stages {stage("build"){options {timeout(time:5,unit:'MINUTES')retry(3)timestamps()}steps{echo "hello"}}}}
范例:丢弃旧的构建
==💘 实践:options测试(测试成功)-2023.4.8==
- 使用片段生成器生成代码
默认这个流水线的配置这里为空:
- 代码
buildUser ="jenkins"pipeline {agent anyoptions {buildDiscarder logRotator(artifactDaysToKeepStr:'',artifactNumToKeepStr:'',daysToKeepStr:'30',numToKeepStr:'5')} environment {NAME="RongMei"VERSION="1.1.10"ENVTYPE="DEV"} stages {stage("build"){environment {VERSION="1.1.20"}steps {script {echo "${VERSION}"echo "${buildUser}"}}}}}
- 运行
可以看到,运行流水线后,这里的配置被配置上了。
范例:配置流水线跳过默认代码检出功能
💘 实践:配置流水线跳过默认代码检出功能-2023.6.26(测试成功)
- 实验环境
jenkins/jenkins:2.346.3-2-lts-jdk11gitlab/gitlab-ce:15.0.3-ce.0sonarqube:9.9.0-communitySonarScanner4.8.0.2856
实验软件(无)
当前现象
当给jenkins流水线配置了从git仓库拉取Jenkinsfile时(即使用共享库),编译阶段会多了一个默认代码检出
阶段
- 那该如何跳过这个功能呢?
在声明式片段里生成语法下:
options {skipDefaultCheckout true}
- 我们来配置下共享库里的代码,提交,再次构建测试下
提交。
再次构建测试:
构建结果:
符合预期,以上问题已解决。😘
parameters{}
- 参数化构建,在jenkinsfile中以代码的方式定义构建参数;
- 注意由于是通过代码定义的,需运行一次构建后才能生成;
- String、Choice…
结论:
如果同时有
environment
和parameters
时,env.VERSION
和VERSION
默认会使用enironment的值的,因此要想使用parameters,推荐使用params.VERSION
来引用。
- 定义: 流水线在运行时设置的参数,UI页面的参数。所有的参数都存储在params对象中。
- 将web ui页面中定义的参数,以代码的方式定义。 (以代码的方式定义的配置,需要流水线构建运行后才能看到效果)
pipeline {agent anyparameters {string(name:'VERSION',defaultValue:'1.1.1',description:'') }stages {stage("Build"){steps {echo "${params.VERSION}"}}}}
范例:参数构建测试
==💘 实践:parameters测试(测试成功)-2023.4.8==
- 使用片段生成器生成代码
- 代码
buildUser ="jenkins"pipeline {agent anyoptions {buildDiscarder logRotator(artifactDaysToKeepStr:'',artifactNumToKeepStr:'',daysToKeepStr:'30',numToKeepStr:'5')} parameters {choice choices:['prod','test','dev'],description:'Please choice env name.',name:'envName'string defaultValue:'1.1.1',name:'Version',trim:true}environment {NAME="RongMei"VERSION="1.1.10"ENVTYPE="DEV"} stages {stage("build"){environment {VERSION="1.1.20"}steps {script {echo "${VERSION}"echo "${buildUser}"echo "${params.envName}"echo "${env.envName}"echo "${envName}"}}}}}
- 运行
Pipeline核心语法3
trigger{}
-cron定时触发:triggers{cron('H */7 * * 1-5') }(例如代码扫描。)-pollSCM:triggers{pollSCM('H */7 * * 1-5') }##被动地去检查你的代码库是否有更新,如果有更新就触发任务;
范例:cron触发
==💘 实践:trigger测试(测试成功)-2023.4.8==
常见的触发方式:
使用cron触发:
默认trigger这里为空:
- 代码
buildUser ="jenkins"pipeline {agent anyoptions {buildDiscarder logRotator(artifactDaysToKeepStr:'',artifactNumToKeepStr:'',daysToKeepStr:'30',numToKeepStr:'5')} parameters {choice choices:['prod','test','dev'],description:'Please choice env name.',name:'envName'string defaultValue:'1.1.1',name:'Version',trim:true}triggers {cron 'H ****'}environment {NAME="RongMei"VERSION="1.1.10"ENVTYPE="DEV"} stages {stage("build"){environment {VERSION="1.1.20"}steps {script {echo "${VERSION}"echo "${buildUser}"echo "${params.envName}"echo "${env.envName}"echo "${envName}"}}}}}
- 运行
可以看到,trigger这里出现了配置,符合预期。
范例:上游项目构建触发
triggers {upstream 'demo2,'}
我们不会用这种方式来触发,会用一个比较通用的方式来触发的,请看后文。
- 生成代码
buildUser ="jenkins"pipeline {agent anyoptions {buildDiscarder logRotator(artifactDaysToKeepStr:'',artifactNumToKeepStr:'',daysToKeepStr:'30',numToKeepStr:'5')} parameters {choice choices:['prod','test','dev'],description:'Please choice env name.',name:'envName'string defaultValue:'1.1.1',name:'Version',trim:true}triggers {upstream 'demo2,'}environment {NAME="RongMei"VERSION="1.1.10"ENVTYPE="DEV"} stages {stage("build"){environment {VERSION="1.1.20"}steps {script {echo "${VERSION}"echo "${buildUser}"echo "${params.envName}"echo "${env.envName}"echo "${envName}"}}}}}
- 运行后,demo项目里就能看到,符合预期:
input{}
参数解析
- message:提示信息
- ok:表单中确认按钮的文本
- submitter:提交人,默认所有人可以
- parameters: 交互时用户选择的参数
小demo:
pipeline {agent anystages {stage('Deploy') {input {message "是否继续发布"ok "Yes"submitter "zeyang,aa"parameters {string(name:'ENVTYPE',defaultValue:'DEV',description:'env type..[DEV/STAG/PROD]')}}steps {echo "Deploy to ${ENVTYPE},doing......."}}}}
==💘 实践:input测试(测试成功)-2023.4.8==
- 使用声明式语法生成器生成代码
- 代码
buildUser ="jenkins"pipeline {agent anyoptions {buildDiscarder logRotator(artifactDaysToKeepStr:'',artifactNumToKeepStr:'',daysToKeepStr:'30',numToKeepStr:'5')} parameters {choice choices:['prod','test','dev'],description:'Please choice env name.',name:'envName'string defaultValue:'1.1.1',name:'Version',trim:true}triggers {cron 'H ****'}environment {NAME="RongMei"VERSION="1.1.10"ENVTYPE="DEV"} stages {stage("build"){environment {VERSION="1.1.20"}input {message 'Please choice your options.'ok '提交'parameters {choice choices:['rollback','stop'],name:'runOptions'}}steps {script {echo "${VERSION}"echo "${buildUser}"echo "${params.envName}"echo "${env.envName}"echo "${envName}"echo "${runOptions}"if("${runOptions}"=="rollback"){println("rollback……")}if("${runOptions}"=="stop"){println("stop……")} }}}}}
- 运行
选择rollback:
选择stop:
- 注意:
when{}
根据条件判断是否运行Stage;
判断条件
- 根据环境变量判断
- 根据表达式判断
- 根据条件判断(not/allOf/anyOf)
pipeline {agent anystages {stage('Build') {steps {echo 'build......'}}stage('Deploy') {when {environment name:'DEPLOY_TO',value:'DEV'}steps {echo 'Deploying.......'}}}}### allOf 条件全部成立when {allOf {environment name:'CAN_DEPLOY',value:'true'environment name:'DEPLOY_ENV',value:'dev'}}### anyOf 条件其中一个成立when {anyOf {environment name:'CAN_DEPLOY',value:'true'environment name:'DEPLOY_ENV',value:'dev'}}
范例:根据环境变量去配置
- 代码:
pipeline {agent anystages {stage('Build') {steps {echo 'build......'}}stage('Deploy') {when {environment name:'DEPLOY_TO',value:'DEV'}steps {echo 'Deploying.......'}}}}
运行:(因为默认是没这个变量的,Deploy阶段肯定跳过的)
- 添加
DEPLOY_TO
变量后,再次测试
pipeline {agent anyenvironment {DEPLOY_TO='DEV'}stages {stage('Build') {steps {echo 'build......'}}stage('Deploy') {when {environment name:'DEPLOY_TO',value:'DEV'}steps {echo 'Deploying.......'}}}}
再次运行:
范例
==💘 实践:when测试(测试成功)-2023.4.8==
- 代码
buildUser ="jenkins"pipeline {agent anyoptions {buildDiscarder logRotator(artifactDaysToKeepStr:'',artifactNumToKeepStr:'',daysToKeepStr:'30',numToKeepStr:'5')} parameters {choice choices:['prod','test','dev'],description:'Please choice env name.',name:'envName'string defaultValue:'1.1.1',name:'Version',trim:true}triggers {cron 'H ****'}environment {NAME="RongMei"VERSION="1.1.10"ENVTYPE="DEV"} stages {stage("build"){environment {VERSION="1.1.20"}input {message 'Please choice your options.'ok '提交'parameters {choice choices:['rollback','stop'],name:'runOptions'}}steps {script {echo "${VERSION}"echo "${buildUser}"echo "${params.envName}"echo "${env.envName}"echo "${envName}"echo "${runOptions}"if("${runOptions}"=="rollback"){println("rollback……")}if("${runOptions}"=="stop"){println("stop……")} env.runOptions ="${runOptions}"}}}stage("Rollback"){when {environment name:"runOptions",value:'rollback'}steps {println("rollback……")}}}}
- 运行
当选择rollback时:Rollback阶段被执行
当选择stop时:Rollback阶段被跳过
prallel{}
Stage并行运行
场景: 自动化测试,多主机并行发布。
pipeline {agent anystages {stage('Parallel Stage') {failFast trueparallel {stage('windows') {agent {label "master"}steps {echo "windows"}}stage('linux') {agent {label "build"}steps {echo "linux"}}}}}}
==💘 实践:parallel测试(测试成功)-2023.4.10==
- 代码
pipeline {agent anystages {stage('Parallel Stage') {failFast trueparallel {stage('windows') {agent {label "master"}steps {echo "windows"}}stage('linux') {agent {label "build"}steps {echo "linux"}}}}}}
- 运行
FAQ:如何解决并发构建中的workspace问题?
这个workspace问题挺重要的。(已解决)
多个并行阶段使用一个工作目录肯定是有问题的;(在每次构建的时候加一个随机数)
1.build_id
2.使用随机数
或者直接禁用并行构建方法也行;
并行构建用到的场景:
情况1:如果一个项目管理多个微服务,那么就会需要用到并行构建; 情况2:多分支;
方法1:build_id
env.nworkspace ="/opt/agent/test/build-${BUILD_ID}"pipeline{agent{node{label "build"customWorkspace "${env.nworkspace}"}}stages{stage("build"){steps{echo "${env.nworkspace}"}}}}
多点击几次运行:
方法2:随机数
env.nworkspace ="/opt/agent/test/${JOB_NAME}-${UUID.randomUUID().toString()}"pipeline{agent{node{label "build"customWorkspace "${env.nworkspace}"}}stages{stage("build"){steps{echo "${env.nworkspace}"}}}}### 输出demo-fec54ca7-81a5-452e-91b5-2a187ab3562b
- 扩展:
2023.4.10-实践 - Jenkins 声明式管道中的动态并行阶段(简悦剪藏)
二、Groovy
1、Groovy简介
groovy这一块我们基本只会用到数据处理;
使用groovy的目的:
- 想让自己的pipeline更加灵活;
- 搞微服务实践时,会调api,或者做数据处理;
Groovy功能强大,可选类型和动态语言(很多语法和python类似),天生支持Java平台。
简洁且简单易学的语法。
可以与任何Java程序顺利集成、包括脚本编写功能、特定领域语言编写,运行时和编译时元编程以及函数式编程。
Groovy参考文档
实验软件
- 3️⃣ 验证
[root@devops~]#groovyshApr21,202211:25:39AMjava.util.prefs.FileSystemPreferences$1runINFO:Createduser preferences directory.GroovyShell(4.0.1,JVM:1.8.0_322)Type':help'or ':h'forhelp.groovy:000>println("hello world")hello world===>nullgroovy:000>
2、Groovy数据类型
1.String
字符串类型, 是在流水线中应用最为广泛的一种类型。
字符串定义
可以通过双引号、单引号、三引号定义;
- 如果是普通字符串: 用单引号
- 如果存在字符串插值(变量): 用双引号
- 多引号:可以往里面放字符串片段。
Stringname ="zhangsan"Stringname ='zhangsan'Stringzeyang ="""devops"""println(zeyang)
⚠️ 注意:多引号里面可以对变量进行换行操作。
等价于:
字符串常见操作方法
name ="zeyang"pipeline {agent anystages{stage("run"){steps{script{println(name)job_name ="devops05-app-service_CI"println(job_name.indexOf("-"))bu_name =job_name.split('-')[0] println(bu_name) println(job_name.contains("CI"))println("size:${job_name.size()}")println("length:${job_name.length()}")println("enswith CI:${job_name.endsWith('CI')}")Stringnums ="1234567"println(nums.reverse()) Stringlog ="error:xxxxxx aa"println(log.minus("a")) println(log -"a") println(log +"a") println(log.plus("aa")) println(job_name.indexOf("-")) name ="xyy"defmessage ="hello ${name}"println(message) println(message.toString()) }}}}}
==💘 实战:groovy字符串测试-2023.4.10(测试成功)==
- 代码
name ="zeyang"pipeline {agent anystages{stage("run"){steps{script{println(name)job_name ="devops05-app-service_CI"bu_name =job_name.split('-')[0]println(bu_name) println(job_name.contains("CI"))println("size:${job_name.size()}")println("length:${job_name.length()}")println("enswith CI:${job_name.endsWith('CI')}")}}}}}
- 运行
- 官方文档:
2.List
列表常见操作方法
tools =["gitlab","jenkins","maven","sonar"]pipeline {agent anystages{stage("run"){steps{script{listName =["hg","xyy","happy"]newListName =[1,2,3,4] asint[]println(listName)println(newListName)println(tools)println(tools +"k8s") println(tools <<"ansible") println(tools -"maven") println(tools)tools.add("maven") println(tools)println(tools.getClass())println(tools.contains("jenkins"))println(tools.size())println(tools[0])println(tools[-1])println(tools.isEmpty())println(tools.unique())println(tools.reverse())println(tools.sort())println(tools.count("jenkins"))}}}}}
==💘 实战:groovy列表测试-2023.4.10(测试成功)==
- 代码
tools =["gitlab","jenkins","maven","sonar"]pipeline {agent anystages{stage("run"){steps{script{println(tools)println(tools +"k8s")println(tools)println(tools <<"ansible") println(tools)println(tools -"maven")println(tools)tools.add("maven")println(tools)println(tools.getClass())println(tools.contains("jenkins"))println(tools.size())println(tools[0])println(tools[-1])}}}}}
- 运行
- 官方文档:
3.map
map常见方法
user_info =["id":100,"name":"jenkins"]pipeline {agent anystages{stage("run"){steps{script{println(user_info)println(user_info["name"])println(user_info["id"])println(user_info.get("id")) user_info["name"] ="jenkinsX"println(user_info)println(user_info.containsKey("name"))println(user_info.containsValue(100))println(user_info.keySet())user_info.remove("name")println(user_info)}}}}}
==💘 实战:groovy字典测试-2023.4.10(测试成功)==
- 代码
·
user_info =["id":100,"name":"jenkins"]pipeline {agent anystages{stage("run"){steps{script{println(user_info)println(user_info["name"])println(user_info["id"])user_info["name"] ="jenkinsX"println(user_info)println(user_info.containsKey("name"))println(user_info.containsValue(100))println(user_info.keySet()) user_info.remove("name")println(user_info)}}}}}
- 运行
Groovy常用函数
- 打印变量类型
3、Groovy条件语句
1.if语句
branchName ="dev"pipeline {agent anystages{stage("run"){steps{script {currentBuild.displayName =branchNameif(branchName =="dev"){println("deploy to dev....")currentBuild.description ="deploy to dev...."} elseif(branchName =="master"){println("deploy to stag....")currentBuild.description ="deploy to stag...."} else{currentBuild.description ="error..."println("error...")}}}}}}
==💘 实战:groovy if语句测试-2023.4.10(测试成功)==
- 代码
branchName ="dev"pipeline {agent anystages{stage("run"){steps{script {currentBuild.displayName =branchNameif(branchName =="dev"){println("deploy to dev....")currentBuild.description ="deploy to dev...."} elseif(branchName =="master"){println("deploy to stag....")currentBuild.description ="deploy to stag...."} else{currentBuild.description ="error..."println("error...")}}}}}}
- 运行
2.switch语句
branchName ="dev"pipeline {agent anystages{stage("run"){steps{script {currentBuild.displayName =branchNameswitch(branchName) {case"dev":println("deploy to dev....")currentBuild.description ="deploy to dev...."breakcase"master":println("deploy to stag....")currentBuild.description ="deploy to stag...."breakdefault:currentBuild.description ="error..."println("error...")break}}}}}}
==💘 实战:groovy switch语句测试-2023.4.10(测试成功)==
- 代码
branchName ="dev"pipeline {agent anystages{stage("run"){steps{script {currentBuild.displayName =branchNameswitch(branchName) {case"dev":println("deploy to dev....")currentBuild.description ="deploy to dev...."breakcase"master":println("deploy to stag....")currentBuild.description ="deploy to stag...."breakdefault:currentBuild.description ="error..."println("error...")break}}}}}}
- 运行
3.for循环
users =[["name":"zeyang","role":"dev"],["name":"zeyang1","role":"admin"],["name":"zeyang2","role":"ops"],["name":"zeyang3","role":"test"]]pipeline {agent anystages{stage("test"){steps{script{for(i=1;i<10;i++){println(i)} }}}stage("run"){steps{script {user_names =[]for(i inusers){println(i["name"])user_names <<i["name"]}println(user_names) 10.times {println('hello')}10.times {i->println(i)} }}}}}
==💘 实战:groovy for语句测试-2023.4.10(测试成功)==
- 代码
users =[["name":"zeyang","role":"dev"],["name":"zeyang1","role":"admin"],["name":"zeyang2","role":"ops"],["name":"zeyang3","role":"test"]]pipeline {agent anystages{stage("run"){steps{script {user_names =[]for(i inusers){println(i["name"])user_names <<i["name"]}println(user_names) 10.times {println('hello')}10.times {i->println(i)}}}}}}
- 运行
4.while循环
sleeps =truepipeline {agent any stages{stage("run"){steps{script {while(sleeps){println("sleep....")}}}}}}
==💘 实战:groovy while语句测试-2023.4.10(测试成功)==
- 代码
sleeps =truepipeline {agent any stages{stage("run"){steps{script {while(sleeps){println("sleep....")}}}}}}
- 运行
4、Groovy异常处理与函数
1.异常处理
try
catch
finally
pipeline{agent anystages{stage("run"){steps{script{try{println(a) } catch(Exceptione){println(e)} finally{println("always....")}}}}}}
==💘 实战:groovy 异常处理测试-2023.4.10(测试成功)==
代码:
pipeline{agent anystages{stage("run"){steps{script{println(a) }}}}}
运行:(肯定会报错的)
加上异常处理代码后,再次运行:(就能看到会捕获并抛出异常,流水线执行成功,符合预期)
pipeline{agent anystages{stage("run"){steps{script{try{println(a) } catch(Exceptione){println(e)} finally{println("always....")}}}}}}
2.函数
函数定义
函数传参
函数返回值
函数调用
users =[["id":1,"name":"jenkins1"],["id":2,"name":"jenkins2"],["id":3,"name":"jenkins3"],]pipeline{agent any stages{stage("run"){steps{script {name =GetUserNameByID(1)println(name) }}}}}defGetUserNameByID(id){for(i inusers){if(i["id"] ==id){returni["name"]}}return"null"}
==💘 实战:groovy 函数测试-2023.4.10(测试成功)==
- 代码
users =[["id":1,"name":"jenkins1"],["id":2,"name":"jenkins2"],["id":3,"name":"jenkins3"],]pipeline{agent any stages{stage("run"){steps{script {name =GetUserNameByID(1)println(name) }}}}}defGetUserNameByID(id){for(i inusers){if(i["id"] ==id){returni["name"]}}return"null"}
- 运行
关于我
我的博客主旨:
- 排版美观,语言精炼;
- 文档即手册,步骤明细,拒绝埋坑,提供源码;
- 本人实战文档都是亲测成功的,各位小伙伴在实际操作过程中如有什么疑问,可随时联系本人帮您解决问题,让我们一起进步!
🍀 微信二维码 x2675263825 (舍得), qq:2675263825。
🍀 微信公众号 《云原生架构师实战》
🍀 语雀